﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Continuum.Classes;
using System.Xml.Serialization;
using System.IO;
using ZedGraph;

//
// Continuum : An open-source financial simulation program written in c#
//
// It is meant to model your primary financial instruments over time
//
// It uses the concepts of Funds and Actors and Historical Balances
// to render financial projections.
//
// Although i intend to emphasise personal finance modeling, it has room
// for growth into larger macro type financial simulations.
//
// The focus of this project is not on strict mathmatical precision, but rather to allow
// easy modeling and analysis at variable Level Of Detail granularities. 
//

namespace Continuum
{
    public partial class MainForm : Form
    {
        #region Member Variables
        public const string BuildNumber = "1.2.0";
        public static MainForm InstanceRef;
        public bool IsLoading = false;  // supress event handlers on form load
        private DateTime LastDate = DateTime.Now.Date;

        public static bool IsEncrypted = false;
        public static string EncryptPassword = ""; // retain password in memory so that password only asked once per run

        public static Projector projector = new Projector();
        public ContinuumSettings settings = new ContinuumSettings();
        public static MarketEngine market = new MarketEngine();

        public static string ConfigDir { get { return Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments ) + "\\Continuum\\"; } }
        public static string ConfigFile { get { return ConfigDir + "Continuum-Settings.xml"; } }

        public bool HasChanged = false;

        #endregion

        #region Common Form Methods

        public MainForm()
        {
            InitializeComponent();
            InstanceRef = this;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            txtConsole.Text += "Continuum Build " + BuildNumber + " started " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString() + "\r\n";

            this.Text = "Continuum (Build " + BuildNumber + ")";
            SetupTooltips();

            #region Setup Config Subdirectory

            // Optional... relocate old data files
            //if (Directory.Exists(OldConfigDir))
            //{
            //}

            if (!Directory.Exists(ConfigDir))
            {
                Directory.CreateDirectory(ConfigDir);
            }

            #endregion

            #region Load Default Continuum, if one exists

            // If settings file exists, then read it
            if (File.Exists(ConfigFile))
            {
                settings = ContinuumSettings.LoadSettings(ConfigFile);

                // if the ActiveUniverse doesnt exist, create default one, save and update settings
                if (!File.Exists(settings.ActiveUniverse)) 
                {
                    EditProjector ep = new EditProjector();
                    ep.SetProjectorReference(projector);
                    ep.SaveAsMode();
                    ep.ShowDialog();

                    if (projector.UniverseName == "") return;

                    SaveContinuum();
                }
            }
            else
            {
                EditProjector ep = new EditProjector();
                ep.SetProjectorReference(projector);
                ep.SaveAsMode();
                ep.ShowDialog();

                if (projector.UniverseName == "") return;

                SaveContinuum();
            }

            LoadContinuum(settings.ActiveUniverse);

            #endregion

            #region SetupGraphPane

            IsLoading = true;

            dateTimePicker1.Value = DateTime.Now.Date.AddMonths(projector.PastWindow * -1);
            dateTimePicker2.Value = DateTime.Now.Date.AddMonths(projector.FutureWindow);

            IsLoading = false;

            GraphPane myPane = zedGraphFundActivity.GraphPane;
            myPane.Title.Text = "Continuum Projection for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            myPane.XAxis.Title.Text = "Time";
            myPane.XAxis.Type = AxisType.Date;
            myPane.YAxis.Title.Text = "Fund Values";
            myPane.YAxis.Scale.Mag = 0;
            myPane.Fill = new Fill(Color.White, Color.Goldenrod, 45.0f);
            myPane.Chart.Fill = new Fill(Color.White, Color.Goldenrod, 45.0F);

            #endregion

            #region Setup FundDisbursal GraphPane

            GraphPane fdPane = zedGraphControl2.GraphPane;
            fdPane.Title.Text = "Fund Disbursal Chart";
            fdPane.Fill = new Fill(Color.White, Color.Gray, 45.0F);
            fdPane.Chart.Fill = new Fill(Color.White, Color.Gray, 45.0F);
            fdPane.XAxis.IsVisible = false;
            fdPane.YAxis.IsVisible = false;
            //fdPane.Legend.FontSpec.Size = 14f;
            fdPane.IsFontsScaled = false;


            #endregion

            #region Setup StackedActivity Graph

            GraphPane fdPaneSV = zedGraphStackedValue.GraphPane;
            fdPaneSV.Title.Text = "Stacked Value Graph" + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString(); 
            fdPaneSV.XAxis.Title.Text = "Time";
            fdPaneSV.XAxis.Type = AxisType.Date;
            fdPaneSV.YAxis.Title.Text = "Fund Values";
            fdPaneSV.YAxis.Scale.Mag = 0;
            fdPaneSV.YAxis.Scale.Min = 0;
            fdPaneSV.Fill = new Fill(Color.White, Color.ForestGreen, 45.0F);
            fdPaneSV.Chart.Fill = new Fill(Color.White, Color.SlateGray, 45.0F);

            #endregion

            #region Setup StackedDebt Graph

            GraphPane fdPaneSD = zedGraphStackedDebt.GraphPane;
            fdPaneSD.Title.Text = "Stacked Debt Graph" + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            fdPaneSD.XAxis.Title.Text = "Time";
            fdPaneSD.XAxis.Type = AxisType.Date;
            fdPaneSD.YAxis.Title.Text = "Fund Values";
            fdPaneSD.YAxis.Scale.Mag = 0;
            fdPaneSD.YAxis.Scale.Min = 0;
            fdPaneSD.Fill = new Fill(Color.White, Color.RosyBrown, 135.0F);
            fdPaneSD.Chart.Fill = new Fill(Color.White, Color.LightSlateGray, 135.0F);

            #endregion

            #region Setup Stock Unit Value Graph
            myPane = zedGraphStockUnitValues.GraphPane;
            myPane.Title.Text = "Stock Unit Values for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            myPane.XAxis.Title.Text = "Time";
            myPane.XAxis.Type = AxisType.Date;
            myPane.YAxis.Title.Text = "Unit Values";
            myPane.YAxis.Scale.Mag = 0;
            myPane.YAxis.Scale.Min = 0;
            myPane.Fill = new Fill(Color.White, Color.Goldenrod, 45.0f);
            myPane.Chart.Fill = new Fill(Color.White, Color.Goldenrod, 45.0F);
            #endregion

            #region Setup Stock Value Graph
            myPane = zedGraphStockValues.GraphPane;
            myPane.Title.Text = "Stock Values for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            myPane.XAxis.Title.Text = "Time";
            myPane.XAxis.Type = AxisType.Date;
            myPane.YAxis.Title.Text = "Investment Values";
            myPane.YAxis.Scale.Mag = 0;
            myPane.YAxis.Scale.Min = 0;
            myPane.Fill = new Fill(Color.White, Color.Goldenrod, 45.0f);
            myPane.Chart.Fill = new Fill(Color.White, Color.Goldenrod, 45.0F);
            #endregion

            #region Setup Distribution Graph

            fdPane = zedGraphControlDistribution.GraphPane;
            fdPane.Title.Text = "Simple Distribution Chart";
            fdPane.Fill = new Fill(Color.White, Color.Gray, 45.0F);
            fdPane.Chart.Fill = new Fill(Color.White, Color.Gray, 45.0F);
            fdPane.XAxis.IsVisible = false;
            fdPane.YAxis.IsVisible = false;
            //fdPane.Legend.FontSpec.Size = 14f;
            fdPane.IsFontsScaled = false;
            zedGraphControlDistribution.IsShowPointValues = true;

            #endregion

            #region Automaticall Run/Sync Simulation

            //if (market.WebRefresh()) market.SaveMarket();
            timerDetectDatechange.Enabled = true;

            if (market.MarketFunds.Count > 0 && projector.AutoRefreshMarket)
            {
                if (market.LastRefreshed == DateTime.MinValue)
                {
                    timerRefreshMarket.Interval = 15000;
                    timerRefreshMarket.Enabled = true;
                }
                else
                {
                    TimeSpan ts = DateTime.Now.Date - market.LastRefreshed.Date;

                    if (ts.Days >= projector.AutoRefreshRate)
                    {
                        timerRefreshMarket.Interval = 15000;
                        timerRefreshMarket.Enabled = true;
                    }
                }
            }

            RunProjection(true);


            #endregion

            // For 1024x600 netbooks, go ahead and maximize screen
            if (SystemInformation.VirtualScreen.Width == 1024 && SystemInformation.VirtualScreen.Height == 600)
                WindowState = FormWindowState.Maximized;
        }

        private void SetupTooltips()
        {
            System.Windows.Forms.ToolTip tip1 = new ToolTip();
            tip1.SetToolTip(lblSavingsChange, "This represents the combined growth of all funds categorized as Savings");

            System.Windows.Forms.ToolTip tip2 = new ToolTip();
            tip2.SetToolTip(lblDebtChange, "This represents the combined change of all funds categorized as Debt");

            System.Windows.Forms.ToolTip tip3 = new ToolTip();
            tip3.SetToolTip(lblNetValueChange, "This represents added value plus debt reduction");

            System.Windows.Forms.ToolTip tip4 = new ToolTip();
            tip4.SetToolTip(lblNetWaste, "This represents the undistributed (future) growth of spending accounts.");

            System.Windows.Forms.ToolTip tip5 = new ToolTip();
            tip5.SetToolTip(lblNetValue, "This represents the value of all funds plus debt equity.");

            System.Windows.Forms.ToolTip tip6 = new ToolTip();
            tip6.SetToolTip(lblNetDebt, "This represents the combined debt of all debt funds.");

            System.Windows.Forms.ToolTip tip7 = new ToolTip();
            tip6.SetToolTip(lblNetIndex, "When this reaches 1.0 your value/debt is equal.\r\nWhen this reaches 2.0 you are debt free.");

            System.Windows.Forms.ToolTip tip8 = new ToolTip();
            tip6.SetToolTip(lblNetDebt2, "This represents the combined debt of all debt funds.");

        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (HasChanged)
            {
                if (MessageBox.Show("Do you want to save your changes to the Continuum?", "Changes made will be lost", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes)
                    SaveContinuum();
            }
        }

        #endregion

        #region Graphing Methods

        private void RenderGraph(DateTime StartDate, DateTime EndDate)
        {
            if (tabControlGraph.SelectedTab == tabPageFundActivity)
            {
                RenderActivityGraph(StartDate, EndDate);

                // I'm not sure where how i want stocks to play into Continuum
                // I think its probably best to keep separate from other funds
                // Currently they dont support actors and qty of shares is constant
                // so it might confuse activity graph
                // They have their own graphs but if i want to include in fund activity graph
                // later i can just uncomment the lines below

                //if (market.MarketFunds.Count > 0) 
                //   RenderActivityStocks(StartDate, EndDate);
            }

            if (tabControlGraph.SelectedTab == tabPageStockUnitValues)
            {
                RenderStockUnitGraph(StartDate, EndDate);
            }

            if (tabControlGraph.SelectedTab == tabPageStockValues)
            {
                RenderStockGraph(StartDate, EndDate);
            }

            if (tabControlGraph.SelectedTab == tabPageStackedIncome)
                RenderStackedIncomeGraph(StartDate, EndDate);

            if (tabControlGraph.SelectedTab == tabPageStackedDebt)
                RenderStackedDebtGraph(StartDate, EndDate);

            if (tabControlGraph.SelectedTab == tabPageFundDisbursal)
                RenderFundDisbursalGraph();
        }

        private void RenderActivityGraph(DateTime StartDate, DateTime EndDate)
        {
            TimeSpan ts = EndDate - StartDate;
            GraphPane myPane = zedGraphFundActivity.GraphPane;
            myPane.CurveList.Clear();
            zedGraphFundActivity.IsShowPointValues = true;
            myPane.XAxis.Type = AxisType.Date;

            // Pad (only) a few extra days to scale so that it fills out the graph well
            myPane.XAxis.Scale.Min = StartDate.AddDays(-ts.TotalDays/14).ToOADate();
            myPane.XAxis.Scale.Max = EndDate.AddDays(ts.TotalDays/14).ToOADate();
            myPane.Title.Text = projector.UniverseName + " Projection for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            myPane.YAxis.Scale.Mag = 0;

            string tooltip;

            int i = 0;

            // Re-aligned code around activities instead of TriggerDates
            foreach (Fund fund in projector.Funds)
            {
                if (fund.IsHidden) continue;

                PointPairList pplist = new PointPairList();

                foreach (Activity activity in projector.ActivitiesByFund(fund))
                {
                    if (activity.ActivityDate >= StartDate)  // New filtering to decouple projection window from render window
                    {
                        Balance FundBalance = null;

                        if (activity.ActivityType == ActivityTypeEnum.Actor)
                        {
                            if (activity.AffectingActor.ActorFundPrimary == fund.Name)
                                FundBalance = activity.RenderedBalancePrimary;
                            if (activity.AffectingActor.ActorFundSecondary == fund.Name)
                                FundBalance = activity.RenderedBalanceSecondary;
                        }

                        if (activity.ActivityType == ActivityTypeEnum.FundReconcile &&
                            activity.AffectingFund == fund)
                            FundBalance = activity.RenderedBalancePrimary;

                        // If the fund is referenced by this activity, then add balance to plot
                        if (FundBalance != null)
                        {
                            tooltip = FundBalance.BalanceDate.ToShortDateString() + "\n$" + FundBalance.Amount.ToString() + "\n" + FundBalance.Description;
                            pplist.Add(new PointPair(FundBalance.BalanceDate.ToOADate(), (double)FundBalance.Amount, tooltip));
                        }
                    }
                }


                Color c = Color.Red;
                switch (i++)
                {
                    case 0: c = Color.Red; break;
                    case 1: c = Color.Green; break;
                    case 2: c = Color.Blue; break;
                    case 3: c = Color.Firebrick; break;
                    case 4: c = Color.LimeGreen; break;
                    case 5: c = Color.Purple; break;
                    case 6: c = Color.SlateGray; break;
                    case 7: c = Color.Turquoise; break;
                    default: c = Color.Green; break;
                }

                SymbolType st = SymbolType.Triangle;
                switch (fund.FundClass)
                {
                    case FundClassEnum.SpendingAccount: st = SymbolType.VDash; break;
                    case FundClassEnum.Debt: st = SymbolType.TriangleDown; break;
                    case FundClassEnum.DebtEquity: st = SymbolType.Diamond; break;
                    case FundClassEnum.Savings: st = SymbolType.Triangle; break;
                    default: st = SymbolType.Square; break;
                }

                LineItem myCurve = myPane.AddCurve(fund.Name, pplist, c, st);
            }

            zedGraphFundActivity.AxisChange();
            zedGraphFundActivity.Refresh();
        }

        private void RenderActivityStocks(DateTime StartDate, DateTime EndDate)
        {
            TimeSpan ts = EndDate - StartDate;
            GraphPane activityPane = zedGraphFundActivity.GraphPane;

            string tooltip;

            int i = 0;

            // Re-aligned code around activities instead of TriggerDates
            foreach (MarketFund fund in market.MarketFunds)
            {
                if (fund.IsHidden) continue;

                PointPairList pplist = new PointPairList();
                PointPairList sulist = new PointPairList();

                foreach (Balance bal in fund.HistoricalBalances)
                {
                    if (bal.BalanceDate >= StartDate)  // New filtering to decouple projection window from render window
                    {
                        tooltip = bal.BalanceDate.ToShortDateString() + "\n$" + bal.Amount.ToString() + " Unit Price\n" + (bal.Amount * fund.Quantity).ToString() + " Total Value\n" + bal.Description;
                        pplist.Add(new PointPair(bal.BalanceDate.ToOADate(), (double) (bal.Amount * fund.Quantity), tooltip));
                    }
                }


                Color c = Color.Red;
                switch (i++)
                {
                    case 0: c = Color.Red; break;
                    case 1: c = Color.Green; break;
                    case 2: c = Color.Blue; break;
                    case 3: c = Color.Firebrick; break;
                    case 4: c = Color.LimeGreen; break;
                    case 5: c = Color.Purple; break;
                    case 6: c = Color.SlateGray; break;
                    case 7: c = Color.Turquoise; break;
                    default: c = Color.Green; break;
                }

                SymbolType st = SymbolType.Triangle;
                LineItem myCurve = activityPane.AddCurve(fund.Name, pplist, c, st);

            }

            zedGraphFundActivity.AxisChange();
            zedGraphFundActivity.Refresh();
        }

        private void RenderStockUnitGraph(DateTime StartDate, DateTime EndDate)
        {
            TimeSpan ts = DateTime.Today - StartDate;
            GraphPane stockunitPane = zedGraphStockUnitValues.GraphPane;
            stockunitPane.CurveList.Clear();
            zedGraphStockUnitValues.IsShowPointValues = true;
            stockunitPane.XAxis.Type = AxisType.Date;

            // Pad (only) a few extra days to scale so that it fills out the graph well
            if (StartDate < DateTime.Today && EndDate > DateTime.Today)
            {
                stockunitPane.XAxis.Scale.Min = StartDate.AddDays(-ts.TotalDays / 14).ToOADate();
                stockunitPane.XAxis.Scale.Max = DateTime.Today.AddDays(ts.TotalDays / 14).ToOADate();
                stockunitPane.Title.Text = projector.UniverseName + " Stock Unit Values for " + dateTimePicker1.Value.ToShortDateString() + " - Today (" +DateTime.Today.ToShortDateString() + ")";
            }
            else
            {
                stockunitPane.XAxis.Scale.Min = StartDate.AddDays(-ts.TotalDays / 14).ToOADate();
                stockunitPane.XAxis.Scale.Max = EndDate.AddDays(ts.TotalDays / 14).ToOADate();
                stockunitPane.Title.Text = projector.UniverseName + " Stock Unit Values for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            }


            string tooltip;

            int i = 0;

            // Re-aligned code around activities instead of TriggerDates
            foreach (MarketFund fund in market.MarketFunds)
            {
                if (fund.IsHidden) continue;

                PointPairList pplist = new PointPairList();

                foreach (Balance bal in fund.HistoricalBalances)
                {
                    if (bal.BalanceDate >= StartDate)  // New filtering to decouple projection window from render window
                    {
                        tooltip = bal.BalanceDate.ToShortDateString() + "\n$" + bal.Amount.ToString() + " Unit Price\n" + bal.Description;
                        pplist.Add(new PointPair(bal.BalanceDate.ToOADate(), (double)bal.Amount, tooltip));
                    }
                }


                Color c = Color.Red;
                switch (i++)
                {
                    case 0: c = Color.Red; break;
                    case 1: c = Color.Green; break;
                    case 2: c = Color.Blue; break;
                    case 3: c = Color.Firebrick; break;
                    case 4: c = Color.LimeGreen; break;
                    case 5: c = Color.Purple; break;
                    case 6: c = Color.SlateGray; break;
                    case 7: c = Color.Turquoise; break;
                    default: c = Color.Green; break;
                }

                SymbolType st = SymbolType.Triangle;
                LineItem myCurve = stockunitPane.AddCurve(fund.Name, pplist, c, st);
            }

            zedGraphStockUnitValues.AxisChange();
            zedGraphStockUnitValues.Refresh();
        }

        private void RenderStockGraph(DateTime StartDate, DateTime EndDate)
        {
            TimeSpan ts = DateTime.Today - StartDate;
            GraphPane stockPane = zedGraphStockValues.GraphPane;
            stockPane.CurveList.Clear();
            zedGraphStockValues.IsShowPointValues = true;
            stockPane.XAxis.Type = AxisType.Date;

            // Pad (only) a few extra days to scale so that it fills out the graph well
            if (StartDate < DateTime.Today && EndDate > DateTime.Today)
            {
                stockPane.XAxis.Scale.Min = StartDate.AddDays(-ts.TotalDays / 14).ToOADate();
                stockPane.XAxis.Scale.Max = DateTime.Today.AddDays(ts.TotalDays / 14).ToOADate();
                stockPane.Title.Text = projector.UniverseName + " Stock Values for " + dateTimePicker1.Value.ToShortDateString() + " - Today (" +DateTime.Today.ToShortDateString() + ")";
            }
            else
            {
                stockPane.XAxis.Scale.Min = StartDate.AddDays(-ts.TotalDays / 14).ToOADate();
                stockPane.XAxis.Scale.Max = EndDate.AddDays(ts.TotalDays / 14).ToOADate();
                stockPane.Title.Text = projector.UniverseName + " Stock Values for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();
            }


            string tooltip;

            int i = 0;

            // Re-aligned code around activities instead of TriggerDates
            foreach (MarketFund fund in market.MarketFunds)
            {
                if (fund.IsHidden) continue;

                PointPairList pplist = new PointPairList();

                foreach (Balance bal in fund.HistoricalBalances)
                {
                    if (bal.BalanceDate >= StartDate)  // New filtering to decouple projection window from render window
                    {
                        tooltip = bal.BalanceDate.ToShortDateString() + "\n$" + bal.Amount.ToString() + " Unit Price\n" + (bal.Amount * fund.Quantity).ToString() + " Total Value\n" + bal.Description;
                        pplist.Add(new PointPair(bal.BalanceDate.ToOADate(), (double) (bal.Amount * fund.Quantity), tooltip));
                    }
                }

                Color c = Color.Red;
                switch (i++)
                {
                    case 0: c = Color.Red; break;
                    case 1: c = Color.Green; break;
                    case 2: c = Color.Blue; break;
                    case 3: c = Color.Firebrick; break;
                    case 4: c = Color.LimeGreen; break;
                    case 5: c = Color.Purple; break;
                    case 6: c = Color.SlateGray; break;
                    case 7: c = Color.Turquoise; break;
                    default: c = Color.Green; break;
                }

                SymbolType st = SymbolType.Triangle;
                LineItem myCurve = stockPane.AddCurve(fund.Name, pplist, c, st);
            }

            zedGraphStockValues.AxisChange();
            zedGraphStockValues.Refresh();
        }

        private void RenderStackedIncomeGraph(DateTime StartDate, DateTime EndDate)
        {
            List<Fund> ValueFunds = projector.GetValueFunds();
            List<Decimal> StackedTotals = new List<decimal>();
            PointPairList[] pplist = new PointPairList[ValueFunds.Count];

            for (int i = 0; i < ValueFunds.Count; i++)
            {
                pplist[i] = new PointPairList();
            }

            DateTime CurrDate = DateTime.MinValue;
            string tooltip = "";

            foreach (Activity activity in projector.Activities)
            {
                // seek up to renderwindow
                if (activity.ActivityDate < StartDate) continue;
                // only process unique dates
                if (activity.ActivityDate == CurrDate) continue;

                CurrDate = activity.ActivityDate;
                Double RunningTotal = 0.0;
                Double CurrAmount = 0.0;
                for (int fundidx = 0; fundidx < ValueFunds.Count; fundidx++)
                {
                    
                    if (ValueFunds[fundidx].FundClass == FundClassEnum.DebtEquity)
                    {
                        CurrAmount = (double) (ValueFunds[fundidx].InceptionBalance.Amount - ValueFunds[fundidx].CalculateBalance(CurrDate).Amount);
                        RunningTotal += CurrAmount;
                        tooltip = ValueFunds[fundidx].Name + " (Equity)\n" + activity.ActivityDate.ToShortDateString() + "\n" + CurrAmount.ToString();
                    }
                    else
                    {
                        CurrAmount = (double)ValueFunds[fundidx].CalculateBalance(CurrDate).Amount;
                        RunningTotal += CurrAmount;
                        tooltip = ValueFunds[fundidx].Name + "\n" + activity.ActivityDate.ToShortDateString() + "\n" + CurrAmount.ToString();
                    }

                    pplist[fundidx].Add(new PointPair(CurrDate.ToOADate(), RunningTotal, tooltip));
                }
            }

            GraphPane myPane = zedGraphStackedValue.GraphPane;
            myPane.CurveList.Clear();
            zedGraphStackedValue.IsShowPointValues = true;
            myPane.XAxis.Type = AxisType.Date;
            myPane.XAxis.Scale.Min = StartDate.ToOADate();
            myPane.XAxis.Scale.Max = EndDate.ToOADate();
            LineItem curve;
            myPane.Title.Text = "Stacked Income Projection for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();

            for (int fundidx = 0; fundidx < ValueFunds.Count; fundidx++)
            {
                Fund f = ValueFunds[fundidx];

                Color c = Color.Red;
                switch (fundidx)
                {
                    case 0: c = Color.Green; break;
                    case 1: c = Color.Blue; break;
                    case 2: c = Color.Firebrick; break;
                    case 3: c = Color.LimeGreen; break;
                    case 4: c = Color.Purple; break;
                    case 5: c = Color.Silver; break;
                    case 6: c = Color.Red; break;
                    default: c = Color.Green; break;
                }

                SymbolType st = SymbolType.Triangle;
                switch (f.FundClass)
                {
                    case FundClassEnum.DebtEquity: st = SymbolType.Diamond; break;
                    case FundClassEnum.Savings: st = SymbolType.Triangle; break;
                    default: st = SymbolType.Square; break;
                }

                curve = myPane.AddCurve(f.Name, pplist[fundidx], c, st);
                curve.Line.Width = 1.5F;
                // Fill the area under the curve with a white-green gradient
                curve.Line.Fill = new Fill(c);
                // Make it a smooth line
                curve.Line.IsSmooth = true;
                curve.Line.SmoothTension = 0.6F;
                // Fill the symbols with white
                curve.Symbol.Fill = new Fill(Color.White);
                curve.Symbol.Size = 10;
            }

            zedGraphStackedValue.AxisChange();
            zedGraphStackedValue.Refresh();

        }

        private void RenderStackedDebtGraph(DateTime StartDate, DateTime EndDate)
        {
            List<Fund> DebtFunds = projector.GetDebtFunds();
            List<Decimal> StackedTotals = new List<decimal>();
            PointPairList[] pplist = new PointPairList[DebtFunds.Count];

            for (int i = 0; i < DebtFunds.Count; i++)
            {
                pplist[i] = new PointPairList();
            }

            DateTime CurrDate = DateTime.MinValue;
            string tooltip = "";

            foreach (Activity activity in projector.Activities)
            {
                // seek up to renderwindow
                if (activity.ActivityDate < StartDate) continue;
                // only process unique dates
                if (activity.ActivityDate == CurrDate) continue;

                CurrDate = activity.ActivityDate;
                Double RunningTotal = 0.0;
                Double CurrAmount = 0.0;
                for (int fundidx = 0; fundidx < DebtFunds.Count; fundidx++)
                {

                    CurrAmount = (double)DebtFunds[fundidx].CalculateBalance(CurrDate).Amount;
                    RunningTotal += CurrAmount;

                    tooltip = DebtFunds[fundidx].Name + "\n" + activity.ActivityDate.ToShortDateString() + "\n" + CurrAmount.ToString();
                    pplist[fundidx].Add(new PointPair(CurrDate.ToOADate(), RunningTotal, tooltip));
                }
            }

            GraphPane myPane = zedGraphStackedDebt.GraphPane;
            myPane.CurveList.Clear();
            zedGraphStackedDebt.IsShowPointValues = true;
            myPane.XAxis.Type = AxisType.Date;
            myPane.XAxis.Scale.Min = StartDate.ToOADate();
            myPane.XAxis.Scale.Max = EndDate.ToOADate();
            LineItem curve;
            myPane.Title.Text = "Debt Projection for " + dateTimePicker1.Value.ToShortDateString() + " - " + dateTimePicker2.Value.ToShortDateString();

            for (int fundidx = 0; fundidx < DebtFunds.Count; fundidx++)
            {
                Fund f = DebtFunds[fundidx];

                Color c = Color.Red;
                switch (fundidx)
                {
                    case 0: c = Color.IndianRed; break;
                    case 1: c = Color.DarkOliveGreen; break;
                    case 2: c = Color.Firebrick; break;
                    case 3: c = Color.LimeGreen; break;
                    case 4: c = Color.Purple; break;
                    case 5: c = Color.Silver; break;
                    case 6: c = Color.Red; break;
                    default: c = Color.Green; break;
                }

                SymbolType st = SymbolType.Circle;
                switch (f.FundClass)
                {
                    case FundClassEnum.DebtEquity: st = SymbolType.Diamond; break;
                    case FundClassEnum.Savings: st = SymbolType.Triangle; break;
                    default: st = SymbolType.Square; break;
                }

                curve = myPane.AddCurve(f.Name, pplist[fundidx], c, st);
                curve.Line.Width = 1.5F;
                // Fill the area under the curve with a white-green gradient
                curve.Line.Fill = new Fill(c);
                // Make it a smooth line
                curve.Line.IsSmooth = true;
                curve.Line.SmoothTension = 0.6F;
                // Fill the symbols with white
                curve.Symbol.Fill = new Fill(Color.White);
                curve.Symbol.Size = 10;
            }

            zedGraphStackedDebt.AxisChange();
            zedGraphStackedDebt.Refresh();

        }

        private void RenderFundDisbursalGraph()
        {
            GraphPane myPane = zedGraphControl2.GraphPane;
            myPane.CurveList.Clear();

            if (projector.Funds.Count == 0) return;
            Fund f = (Fund) ddlFundDisbursal.SelectedValue;
            if (f == null) return;

            int ColorIndex = 0;
            bool IsDebt = (f.FundClass == FundClassEnum.Debt || f.FundClass == FundClassEnum.DebtEquity);

            Dictionary<string, Transaction> GroupedTransactions = f.GroupTransactions(dateTimePicker1.Value);
            foreach (KeyValuePair<string, Transaction> kvp in GroupedTransactions)
            {
                if (IsDebt)
                {
                    if (chkFundDisburseReversed.Checked) myPane.Title.Text = "Projected Debt Increases";
                    else myPane.Title.Text = "Projected Debt Reductions";
                }
                else
                {
                    if (chkFundDisburseReversed.Checked) myPane.Title.Text = "Projected Fund Inflows";
                    else myPane.Title.Text = "Projected Fund Disbursals";
                }

                // Only add the 'disbursal Actors' (Actors whos cumulative effect was to lower balance)
                if ((chkFundDisburseReversed.Checked ? kvp.Value.Amount * -1 : kvp.Value.Amount) < 0)
                {
                    Color c = Color.Blue;
                    switch (ColorIndex++)
                    {
                        case 0: c = Color.Red; break;
                        case 1: c = Color.Green; break;
                        case 2: c = Color.Blue; break;
                        case 3: c = Color.Firebrick; break;
                        case 4: c = Color.LimeGreen; break;
                        case 5: c = Color.Yellow; break;
                        case 6: c = Color.Purple; break;
                        default: break;
                    }
                    Transaction t = kvp.Value;

                    PieItem segment1 = myPane.AddPieSlice((Double) Math.Abs(t.Amount) * (IsDebt?-1:1), c, Color.White, 45f, 0, kvp.Key);
                }
            }

            zedGraphControl2.Refresh();
        }

        private void RenderDistributionGraph(Decimal NetValue, Decimal NetWaste, Decimal NetInterest)
        {
            GraphPane myPane = zedGraphControlDistribution.GraphPane;
            myPane.CurveList.Clear();

            PieItem segment1 = myPane.AddPieSlice((Double) NetValue, Color.Green, Color.White, 45f, 0, "Net Value per Day");
            PieItem segment2 = myPane.AddPieSlice((Double) NetWaste, Color.Yellow, Color.White, 45f, 0, "Net Waste per Day");
            PieItem segment3 = myPane.AddPieSlice((Double) NetInterest, Color.Red, Color.White, 45f, 0, "Net Interest per Day");

            zedGraphControlDistribution.AxisChange();
            zedGraphControlDistribution.Refresh();
        }

        private void tabControlGraph_SelectedIndexChanged(object sender, EventArgs e)
        {
            RenderGraph(dateTimePicker1.Value.Date, dateTimePicker2.Value.Date);
        }

        #endregion

        #region Create/Load/Save Continuum

        private void CreateContinuum()
        {
            Projector newprojector = new Projector();

            EditProjector ep = new EditProjector();
            ep.SetProjectorReference(newprojector);
            ep.SaveAsMode();
            ep.ShowDialog();

            if (newprojector.UniverseName == "") return;

            market = new MarketEngine();

            projector = newprojector;

            // Notes are only refreshed on load or create
            txtNotes.Text = projector.Notes.Replace("\n", "\r\n");

            SaveContinuum();

            RunProjection(true);
        }

        private bool LoadContinuum(string Filename)
        {
            bool Done = false;

            HasChanged = false;

            if (Filename.EndsWith(".continuumx"))
            {
                IsEncrypted = true;

                while (!Done)
                {
                    EncryptPassword = "";
                    EnterPassword epass = new EnterPassword();
                    epass.ShowDialog();

                    if (EncryptPassword == "") Done = true;
                    else
                    {
                        try
                        {
                            projector = Projector.LoadUniverse(Filename, EncryptPassword);
                            Done = true;
                        }
                        catch (System.Security.Cryptography.CryptographicException ex)
                        {
                            MessageBox.Show("You entered and incorrect password", "Unable to load encypted continuum", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                    }
                }
            }
            else projector = Projector.LoadUniverse(Filename);

            LoadMarket();

            //lblActorCount.Text = projector.Actors.Count.ToString();
            //lblFundCount.Text = projector.Funds.Count.ToString();
            //lblActivityCount.Text = projector.Activities.Count.ToString();

            toolStripStatusLabelCounts.Text =   "Actors : " + projector.Actors.Count.ToString() + 
                                                "  Funds : " + projector.Funds.Count.ToString()  +
                                                "  Activities : " + projector.Activities.Count.ToString();

            toolStripStatusLabel1.Text = "Loaded : " + Filename;
            this.Text = "Continuum (Build " + BuildNumber + ") : " + projector.UniverseName + (IsEncrypted ? " (Encrypted)" : "");
            txtNotes.Text = projector.Notes.Replace("\n", "\r\n");


            settings.ActiveUniverse = Filename;
            settings.SaveSettings(ConfigFile);
            return true;
        }

        private void LoadMarket()
        {
            if (IsEncrypted)
            {
                if (File.Exists(ConfigDir + projector.UniverseName + ".marketx"))
                    market = MarketEngine.LoadMarket(ConfigDir + projector.UniverseName + ".marketx", EncryptPassword);
                else market = new MarketEngine();
            }
            else
            {
                if (File.Exists(ConfigDir + projector.UniverseName + ".market"))
                    market = MarketEngine.LoadMarket(ConfigDir + projector.UniverseName + ".market");
                else market = new MarketEngine();
            }

        }

        private void SaveContinuum()
        {
            HasChanged = false;

            projector.Notes = txtNotes.Text;

            string filename = "";

            if (IsEncrypted) filename = projector.SaveUniverse(EncryptPassword);
            else filename = projector.SaveUniverse();

            settings.ActiveUniverse = filename;
            settings.SaveSettings(ConfigFile);

            //lblActorCount.Text = projector.Actors.Count.ToString();
            //lblFundCount.Text = projector.Funds.Count.ToString();
            //lblActivityCount.Text = projector.Activities.Count.ToString();

            toolStripStatusLabelCounts.Text = "Actors : " + projector.Actors.Count.ToString() +
                                    "  Funds : " + projector.Funds.Count.ToString() +
                                    "  Activities : " + projector.Activities.Count.ToString();

            toolStripStatusLabel1.Text = "Loaded : " + filename;
            this.Text = "Continuum (Build " + BuildNumber + ") : " + projector.UniverseName + (IsEncrypted ? " (Encrypted)" : "");
        }

        private void SaveMarket()
        {
            if (IsEncrypted) market.SaveMarket(EncryptPassword);
            else market.SaveMarket();
        }

        #endregion

        #region Menu Handlers

        private void mnuiNewContinuum_Click(object sender, EventArgs e)
        {
            CreateContinuum();
        }

        private void mnuiLoadContinuum_Click(object sender, EventArgs e)
        {
            openFileDialog1.InitialDirectory = ConfigDir;
            openFileDialog1.DefaultExt = ".continuum*";
            openFileDialog1.FileName = "Default.continuum";
            openFileDialog1.Filter = "Continuum Files|*.continuum*|Continuum|*.continuum|Encrypted Continuum |*.continuumx";


            DialogResult dr = openFileDialog1.ShowDialog();
            if (dr != DialogResult.OK) return;

            LoadContinuum(openFileDialog1.FileName);
            RunProjection(true);
        }

        private void mnuiSaveContinuum_Click(object sender, EventArgs e)
        {
            SaveContinuum();
            SaveMarket();
        }

        private void mnuiSaveContinuumAs_Click(object sender, EventArgs e)
        {
            // Currently to reduce possible variations i will force save into Config directory
            // and force name to be the projector's universename (plus file extension)

            EditProjector ep = new EditProjector();
            ep.SetProjectorReference(projector);
            ep.SaveAsMode();
            ep.ShowDialog();

            SaveContinuum();
        }

        private void mnuiExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void mnuiEditProjector_Click(object sender, EventArgs e)
        {
            EditProjector ep = new EditProjector();
            ep.SetProjectorReference(projector);
            ep.ShowDialog();

            RunProjection(false);
        }

        private void mnuiAbout_Click(object sender, EventArgs e)
        {
            AboutForm af = new AboutForm();
            af.Show();
        }

        private void mnuiEditFunds_Click(object sender, EventArgs e)
        {
            EditFunds ef = new EditFunds();
            ef.ShowDialog();

            RunProjection(true);
        }

        private void mnuiEditActors_Click(object sender, EventArgs e)
        {
            EditActors ea = new EditActors();
            ea.ShowDialog();

            RunProjection(false);
        }

        private void mnuiHistoricalBalances_Click(object sender, EventArgs e)
        {
            EditHistoricalBalances ehb = new EditHistoricalBalances();
            ehb.ShowDialog();

            RunProjection(false);
        }

        private void mnuiAddMortgage_Click(object sender, EventArgs e)
        {
            GuidedMortgage gm = new GuidedMortgage();
            gm.ShowDialog();

            RunProjection(true);
        }

        private void mnuiAddChecking_Click(object sender, EventArgs e)
        {
            GuidedChecking gc = new GuidedChecking();
            gc.ShowDialog();

            RunProjection(true);
        }

        private void mnuiAddSavings_Click(object sender, EventArgs e)
        {
            GuidedSavings gs = new GuidedSavings();
            gs.ShowDialog();

            RunProjection(true);
        }

        private void mnuiAddAutoLoan_Click(object sender, EventArgs e)
        {
            GuidedAutoLoan gal = new GuidedAutoLoan();
            gal.ShowDialog();

            RunProjection(true);
        }

        private void mnuiGuidedTransactions_Click(object sender, EventArgs e)
        {
            GuidedTransaction gt = new GuidedTransaction();
            gt.ShowDialog();

            RunProjection(false);
        }

        private void mnuiHelpShortcuts_Click(object sender, EventArgs e)
        {
            MessageBox.Show(
                "Tab Controls :\n\n" +
                "q : Fund Activity Graph\n" +
                "w : Fund Disbursal Graph\n" +
                "e : Stacked Value Graph\n" +
                "r : Stacked Debt Graph\n" +
                "y : Statistics Tab\n" +
                "u : Transactions Tab\n" +
                "i : Balances Tab\n" +
                "o : Activity Tab\n" +
                "p : Notes Tab\n" +
                "ESCAPE : In Notes, this will reenable shortcuts\n\n" + 
                "Date Trim Controls :\n\n" +
                "h : End Date (-1 year)\n" +
                "j : End Date (-1 month)\n" +
                "k : End Date (+1 month)\n" +
                "l : End Date (+1 years)\n" +
                "Shift-H : Start Date (-1 year)\n" +
                "Shift-J : Start Date (-1 month)\n" +
                "Shift-K : Start Date (+1 month)\n" +
                "Shift-L : Start Date (+1 years)\n\n" +
                "SPACE : Toggle Zoom Left Pane", "KeyBoard Shortcuts :",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void mnuiOnlineHelp_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.UseShellExecute = true;
            proc.StartInfo.FileName = "http://project-continuum.com/OnlineHelp";
            proc.Start();
        }

        private void onlineHelpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.UseShellExecute = true;
            proc.StartInfo.FileName = "http://project-continuum.com/OnlineHelp/MainScreen.aspx";
            proc.Start();
        }

        private void mnuiEditMarketFunds_Click(object sender, EventArgs e)
        {
            EditMarketFunds emf = new EditMarketFunds();
            emf.ShowDialog();

            RunProjection(false);

            SaveMarket();
        }

        private void mnuiDownloadFundValues_Click(object sender, EventArgs e)
        {
            DownloadFundPerformance dfp = new DownloadFundPerformance();
            dfp.ShowDialog();

            RunProjection(false);

            SaveMarket();
        }

        private void mnuiRunChromatic_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            //proc.StartInfo.UseShellExecute = true;
            proc.StartInfo.FileName = "chromaticqfx.exe";
            proc.Start();
        }

        #endregion

        #region Projection Management

        public void RefreshLists()
        {
            ddlBalanceFunds.DataSource = null;
            ddlBalanceFunds.DataSource = projector.Funds;
            ddlActivityFunds.DataSource = null;
            ddlActivityFunds.DataSource = projector.Funds;
            ddlTransactionFunds.DataSource = null;
            ddlTransactionFunds.DataSource = projector.Funds;

            if (projector.Funds.Count > 0)
            {
                ddlBalanceFunds.SelectedIndex = 0;
                ddlActivityFunds.SelectedIndex = 0;
                ddlTransactionFunds.SelectedIndex = 0;
            }

        }

        public void RunProjection(bool DoRefreshLists)
        {
            if (IsLoading) return;

            if (DoRefreshLists) RefreshLists();

            // Dont allow window to end before it starts
            if (dateTimePicker2.Value <= dateTimePicker1.Value) dateTimePicker2.Value = dateTimePicker1.Value.AddMonths(1);

            // Make Fund Disbursal remember last selection 
            Fund fdf = (Fund) ddlFundDisbursal.SelectedItem;
            ddlFundDisbursal.DataSource = null;
            ddlFundDisbursal.DataSource = projector.Funds;
            if (fdf != null) ddlFundDisbursal.SelectedItem = fdf;

            projector.ResetVolatile();

            // I Want to see balances (for all funds) on the beginning and end dates even if there is no 
            // activity on those dates, so i will post zero transaction amounts to all funds on those dates.
            foreach (Fund f in projector.Funds)
            {
                Actor start = new Actor(f.Name + " Projection Start (Vol)", f.Name, ActorPeriodicityEnum.OnceOnly, 1, dateTimePicker1.Value, 0.0m);
                Actor end = new Actor(f.Name + " Projection End (Vol)", f.Name, ActorPeriodicityEnum.OnceOnly, 1, dateTimePicker2.Value, 0.0m);
                projector.AddVolatileActor(start);
                projector.AddVolatileActor(end);
                if (DateTime.Today >= dateTimePicker1.Value && DateTime.Today <= dateTimePicker2.Value)
                {
                    Actor today = new Actor(f.Name + " Today (Vol)", f.Name, ActorPeriodicityEnum.OnceOnly, 1, DateTime.Today, 0.0m);
                    projector.AddVolatileActor(today);
                }
            }

            // Need to decouple Projection Window from GraphWindow
            // I was finding actors were not firing outside my time window
            // So i need to Render the Universe from the earliest fund's Inception Date
            // but only RenderGraph for the window chosen
            DateTime dtUniverseInception = dateTimePicker1.Value;
            foreach (Fund fund in projector.Funds)
            {
                if (fund.InceptionBalance.BalanceDate < dtUniverseInception && fund.InceptionBalance.BalanceDate != DateTime.MinValue)
                    dtUniverseInception = fund.InceptionBalance.BalanceDate;
            }

            projector.RenderUniverse(dtUniverseInception, dateTimePicker2.Value);

            RenderGraph(dateTimePicker1.Value, dateTimePicker2.Value);
            RefreshStatistics();
            RefreshBalanceGrid();
            RefreshTransactionGrid();
            RefreshActivityTab();

            toolStripStatusLabelCounts.Text = "Actors : " + projector.Actors.Count.ToString() +
                                    "  Funds : " + projector.Funds.Count.ToString() +
                                    "  Activities : " + projector.Activities.Count.ToString();

        }

        // This timer will detect if the date changes and automatically
        // refresh the projected simulation.  Eventually notifications
        // will be made for activity which occurs today.
        private void timerDetectDatechange_Tick(object sender, EventArgs e)
        {
            if (DateTime.Now.Date == LastDate.Date) return;

            LastDate = DateTime.Now.Date;

            IsLoading = true;

            dateTimePicker1.Value = DateTime.Now.Date.AddMonths(projector.PastWindow * -1);
            dateTimePicker2.Value = DateTime.Now.Date.AddMonths(projector.FutureWindow);

            IsLoading = false;

            RunProjection(false);

            if (market.MarketFunds.Count > 0 && projector.AutoRefreshMarket)
            {
                TimeSpan ts = DateTime.Now.Date - market.LastRefreshed.Date;

                if (ts.Days >= projector.AutoRefreshRate)
                {
                    txtConsole.Text += "Queueing Market Refresh : " + DateTime.Now.ToLongTimeString() + "\r\n";
                    timerRefreshMarket.Interval = 20000;
                    timerRefreshMarket.Enabled = true;
                }
            }
        }

        private void timerRefreshMarket_Tick(object sender, EventArgs e)
        {
            txtConsole.Text += "RefreshMarket Timer : " + DateTime.Now.ToLongTimeString() + "\r\n";

            timerRefreshMarket.Enabled = false;

            try
            {
                if (market.WebRefresh())
                {
                    if (IsEncrypted) market.SaveMarket(EncryptPassword);
                    else market.SaveMarket();
                }

                RunProjection(false);
            }
            catch { }

            txtConsole.Text += "RefreshMarket Timer End : " + DateTime.Now.ToLongTimeString() + "\r\n";

        }

        #endregion

        #region Statistics Tab Management

        public void RefreshStatistics()
        {
            lstFundSummary.Items.Clear();

            Decimal NetValue = 0.0m, NetDebt = 0.0m, NetEquity = 0.0m;

            foreach (Fund fund in projector.Funds)
            {
                if (fund.FundClass == FundClassEnum.Debt || fund.FundClass == FundClassEnum.DebtEquity)
                {
                    if (!fund.IsBenchmark) NetDebt += fund.InterimBalance.Amount;

                    if (fund.FundClass == FundClassEnum.DebtEquity && !fund.IsBenchmark)
                    {
                        NetValue += (fund.InceptionBalance.Amount + fund.EquityOffset) - fund.InterimBalance.Amount;
                        NetEquity += (fund.InceptionBalance.Amount + fund.EquityOffset) - fund.InterimBalance.Amount;
                    }
                }
                else
                {
                    if (fund.FundClass == FundClassEnum.Savings && !fund.IsBenchmark)
                    {
                        NetValue += fund.InterimBalance.Amount;
                    }
                }

                lstFundSummary.Items.Add(fund.Name.PadRight(25) + " : " + fund.InterimBalance.Amount.ToString());
            }

            Decimal BeginningEquity = projector.CalcEquityInitial();
            lblEquityChange.Text = Decimal.Round(NetEquity - BeginningEquity, 2).ToString();

            lblNetEquity.Text = Decimal.Round(NetEquity, 2).ToString();
            lblNetValue.Text = Decimal.Round(NetValue, 2).ToString();
            lblNetDebt.Text = Decimal.Round(NetDebt, 2).ToString();
            lblNetDebt2.Text = Decimal.Round(NetDebt, 2).ToString();

            // Net Index : 0.00 - 1.00 means you owe more than you have equity+savings
            // Net Index : 1.00 - 2.00 as you approach 2.0 you approach zero debt
            // Net Index : 2.00 +      once you have no debt net index cannot be calculated
            if (NetDebt == 0) lblNetIndex.Text = "0.00";
            else
            {
                if (NetValue < NetDebt)
                {
                    lblNetIndex.Text = Decimal.Round(NetValue / NetDebt, 2).ToString();
                }
                else
                {
                    if (NetDebt > 0)
                    {
                        Decimal amt = 1.0m + (1.0m - (NetDebt / NetValue));
                        lblNetIndex.Text = Decimal.Round(amt, 2).ToString();
                    }
                    else
                    {
                        lblNetIndex.Text = "2.00+";
                    }
                }
            }

            // Calculate Overall DetlaShifts in fund classes (Savings, Debt)
            Decimal ValChange = projector.CalculateFundClassDelta(FundClassEnum.Savings, dateTimePicker1.Value);
            Decimal DebtChange = projector.CalculateFundClassDelta(FundClassEnum.Debt, dateTimePicker1.Value);
            DebtChange += projector.CalculateFundClassDelta(FundClassEnum.DebtEquity, dateTimePicker1.Value);
            Decimal NetValueChange = ValChange - DebtChange;

            lblSavingsChange.Text = ValChange.ToString();
            lblDebtChange.Text = DebtChange.ToString();

            lblNetValueChange.Text = NetValueChange.ToString();

            TimeSpan ts = dateTimePicker2.Value - dateTimePicker1.Value;
            Decimal NetValuePerDay = (Decimal)(NetValueChange / ts.Days);
            lblNetValDay.Text = Decimal.Round(NetValuePerDay, 2).ToString();

            Decimal WasteAmountPerDay = 0.0m;

            // Due to drastic effect on waste calculation which Reconciliations cause...
            // the only way to preseve the accuraccy/usefulness of the Waste calculation is to only include future (projected) transactions
            if (dateTimePicker2.Value > DateTime.Today.Date)
            {
                DateTime dtWasteBegins = dateTimePicker1.Value;
                if (dtWasteBegins < DateTime.Today.Date) dtWasteBegins = DateTime.Today.Date;
                TimeSpan wts = dateTimePicker2.Value - dtWasteBegins;

                Decimal WasteAmount = projector.CalculateFundClassDelta(FundClassEnum.SpendingAccount, dtWasteBegins);
                lblNetWaste.Text = WasteAmount.ToString();
                WasteAmountPerDay = (Decimal) (WasteAmount / wts.Days);
                lblNetWasteDay.Text = Decimal.Round(WasteAmountPerDay, 2).ToString();
            }
            else
            {
                lblNetWaste.Text = "N/A";
                lblNetWasteDay.Text = "N/A";
            }

            // Calculate Interest
            Decimal InterestAmount = projector.CalculateInterestOverhead(dateTimePicker1.Value);
            Decimal InterestAmountDay = InterestAmount / ts.Days;
            lblNetInterest.Text = InterestAmount.ToString();
            lblNetInterestDay.Text = Decimal.Round(InterestAmountDay, 2).ToString();

            Decimal InterestMonth = Decimal.Round((Decimal)(InterestAmountDay * 30.5m), 2);
            Decimal InterestYear = Decimal.Round((Decimal)(InterestAmountDay * 365), 2);

            Decimal NetVolumePerDay = WasteAmountPerDay + NetValuePerDay + InterestAmountDay;
            lblTotalVolumeDay.Text = Decimal.Round(NetVolumePerDay, 2).ToString();
            lblTotalVolumeMonth.Text = Decimal.Round(NetVolumePerDay * 30.5m, 2).ToString();
            lblTotalVolumeYear.Text = Decimal.Round(NetVolumePerDay * 365, 2).ToString();

            RenderDistributionGraph(Decimal.Round(NetValuePerDay, 2), 
                                    Decimal.Round(WasteAmountPerDay, 2), 
                                    Decimal.Round(InterestAmountDay, 2));

            Decimal TotalSavings = 0.0m;
            foreach (Fund f in projector.Funds)
            {
                if (f.FundClass == FundClassEnum.Savings && !f.IsBenchmark)
                {
                    TotalSavings += f.InterimBalance.Amount;
                }
            }
            lblNetSavings.Text = Decimal.Round(TotalSavings, 2).ToString();
        }

        private void RefreshTransactionGrid()
        {
            dgvTransactions.DataSource = null;

            if (projector.Funds.Count == 0) return;

            Fund f = (Fund) ddlTransactionFunds.SelectedItem;
            if (f == null) return;

            // Grid was throwing weird exceptions on resizing of columns
            // So im flattening the model by casting as balances
            List<Balance> balances = new List<Balance>();
            foreach (Transaction trans in f.VolatileTransactions)
            {
                balances.Add((Balance)trans);
            }

            if (rbTransDescending.Checked) balances.Sort(new BalanceDescendingComparer());

            dgvTransactions.DataSource = balances;
        }

        private void RefreshBalanceGrid()
        {
            dgvBalances.DataSource = null;

            if (projector.Funds.Count == 0) return;

            Fund f = (Fund)ddlBalanceFunds.SelectedItem;
            if (f == null) return;

            DateTime StartDate = dateTimePicker1.Value, EndDate = dateTimePicker2.Value;

            dgvBalances.DataSource = f.CombinedBalances(rbBalanceAscending.Checked);
        }

        private void RefreshActivityTab()
        {
            dgvUpcomingActivity.DataSource = null;
            lstActivity.DataSource = null;

            if (projector.Activities.Count == 0) return;

            if (ddlActivityFunds.SelectedIndex > 0)
            {
                lstActivity.DataSource = null;
                List<Activity> filtered = projector.ActivitiesByFund((Fund) ddlActivityFunds.SelectedItem);
                lstActivity.DataSource = filtered;
                if (filtered.Count > 0) pgActivity.SelectedObject = filtered[0];

            }
            else
            {
                lstActivity.DataSource = null;
                lstActivity.DataSource = projector.Activities;
                if (projector.Activities.Count > 0) pgActivity.SelectedObject = projector.Activities[0];
            }

            List<Activity> upcoming = projector.ActivitiesInRange(DateTime.Today, DateTime.Today.AddDays(projector.UpcomingDays));

            dgvUpcomingActivity.DataSource = upcoming;
        }


        private void lstFunds_SelectedIndexChanged(object sender, EventArgs e)
        {
        }

        private void rbBalanceAscending_CheckedChanged(object sender, EventArgs e)
        {
            RefreshBalanceGrid();
        }

        private void rbBalanceDescending_CheckedChanged(object sender, EventArgs e)
        {
            RefreshBalanceGrid();
        }

        private void ddlFundDisbursal_SelectedIndexChanged(object sender, EventArgs e)
        {
            chkFundDisburseReversed.Checked = false;

            if (projector.Funds.Count == 0) return;
            Fund f = (Fund) ddlFundDisbursal.SelectedValue;
            if (f == null) return;

            if (f.FundClass == FundClassEnum.Savings) chkFundDisburseReversed.Checked = true;

            RenderFundDisbursalGraph();
        }

        private void chkFundDisburseReversed_CheckedChanged(object sender, EventArgs e)
        {
            RenderFundDisbursalGraph();
        }

        private void lstActivity_SelectedIndexChanged(object sender, EventArgs e)
        {
            pgActivity.SelectedObject = lstActivity.SelectedItem;
        }

        private void ddlActivityFunds_SelectedIndexChanged(object sender, EventArgs e)
        {
            RefreshActivityTab();
        }

        private void ddlBalanceFunds_SelectedIndexChanged(object sender, EventArgs e)
        {
            RefreshBalanceGrid();
        }

        private void ddlTransactionFunds_SelectedIndexChanged(object sender, EventArgs e)
        {
            RefreshTransactionGrid();
        }

        private void rbTransAscending_CheckedChanged(object sender, EventArgs e)
        {
            RefreshTransactionGrid();
        }

        private void rbTransDescending_CheckedChanged(object sender, EventArgs e)
        {
            RefreshTransactionGrid();
        }

        #endregion

        #region DateWindowTrimButtons, Toolbar, and Keyboard Shortcuts

        private void btnStartYdec_Click(object sender, EventArgs e)
        {
            dateTimePicker1.Value = dateTimePicker1.Value.AddYears(-1);
            //RunProjection();
        }

        private void btnStartYinc_Click(object sender, EventArgs e)
        {
            dateTimePicker1.Value = dateTimePicker1.Value.AddYears(1);
            //RunProjection();
        }

        private void btnStartMdec_Click(object sender, EventArgs e)
        {
            dateTimePicker1.Value = dateTimePicker1.Value.AddMonths(-1);
            //RunProjection();
        }

        private void btnStartMinc_Click(object sender, EventArgs e)
        {
            dateTimePicker1.Value = dateTimePicker1.Value.AddMonths(1);
            //RunProjection();
        }

        private void btnEndYdec_Click(object sender, EventArgs e)
        {
            dateTimePicker2.Value = dateTimePicker2.Value.AddYears(-1);
            //RunProjection();
        }

        private void btnEndYinc_Click(object sender, EventArgs e)
        {
            dateTimePicker2.Value = dateTimePicker2.Value.AddYears(1);
            //RunProjection();
        }

        private void btnEndMdec_Click(object sender, EventArgs e)
        {
            dateTimePicker2.Value = dateTimePicker2.Value.AddMonths(-1);
            //RunProjection();
        }

        private void btnEndMinc_Click(object sender, EventArgs e)
        {
            dateTimePicker2.Value = dateTimePicker2.Value.AddMonths(1);
            //RunProjection();
        }

        private void btnToggleZoom_Click(object sender, EventArgs e)
        {
            pnlRight.Visible = !pnlRight.Visible;
        }

        private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
        {
            RunProjection(false);
        }

        private void dateTimePicker2_ValueChanged(object sender, EventArgs e)
        {
            RunProjection(false);
        }

        private void btnFullscreen_Click(object sender, EventArgs e)
        {
            if (FormBorderStyle == FormBorderStyle.None)
            {
                FormBorderStyle = FormBorderStyle.Sizable;

                // For 1024x600 netbooks, go ahead and maximize screen
                if (SystemInformation.VirtualScreen.Width == 1024 && SystemInformation.VirtualScreen.Height == 600)
                    WindowState = FormWindowState.Maximized;
                else
                    WindowState = FormWindowState.Normal;

                statusStrip1.Visible = true;
            }
            else
            {
                FormBorderStyle = FormBorderStyle.None;
                WindowState = FormWindowState.Maximized;
                statusStrip1.Visible = false;
            }
        }

        private void MainForm_KeyPress(object sender, KeyPressEventArgs e)
        {
            // Disable shortcuts if writing notes
            if (ActiveControl != txtNotes)
            {
                if (e.KeyChar == (int)Keys.Space)
                {
                    if (ActiveControl == btnToggleZoom) ActiveControl = tabControlGraph;
                    pnlRight.Visible = !pnlRight.Visible;
                    e.Handled = true;
                    return;
                }

                if (e.KeyChar == 'q') tabControlGraph.SelectedTab = tabPageFundActivity;
                if (e.KeyChar == 'w') tabControlGraph.SelectedTab = tabPageFundDisbursal;
                if (e.KeyChar == 'e') tabControlGraph.SelectedTab = tabPageStackedIncome;
                if (e.KeyChar == 'r') tabControlGraph.SelectedTab = tabPageStackedDebt;
                if ("qwer".Contains(e.KeyChar.ToString())) { e.Handled = true; return; }

                if (e.KeyChar == 'y') tabControlStatistics.SelectedTab = tabPageStatistics;
                if (e.KeyChar == 'u') tabControlStatistics.SelectedTab = tabPageTransactions;
                if (e.KeyChar == 'i') tabControlStatistics.SelectedTab = tabPageBalances;
                if (e.KeyChar == 'o') tabControlStatistics.SelectedTab = tabPageActivity;
                if (e.KeyChar == 'p')
                {
                    tabControlStatistics.SelectedTab = tabPageNotes;
                    txtNotes.SelectionStart = 0;
                    txtNotes.SelectionLength = 0;
                }
                if ("yuiop".Contains(e.KeyChar.ToString())) { e.Handled = true; return; }

                if (e.KeyChar == 'H') dateTimePicker1.Value = dateTimePicker1.Value.AddYears(-1);
                if (e.KeyChar == 'J') dateTimePicker1.Value = dateTimePicker1.Value.AddMonths(-1);
                if (e.KeyChar == 'K') dateTimePicker1.Value = dateTimePicker1.Value.AddMonths(1);
                if (e.KeyChar == 'L') dateTimePicker1.Value = dateTimePicker1.Value.AddYears(1);
                if (e.KeyChar == 'h') dateTimePicker2.Value = dateTimePicker2.Value.AddYears(-1);
                if (e.KeyChar == 'j') dateTimePicker2.Value = dateTimePicker2.Value.AddMonths(-1);
                if (e.KeyChar == 'k') dateTimePicker2.Value = dateTimePicker2.Value.AddMonths(1);
                if (e.KeyChar == 'l') dateTimePicker2.Value = dateTimePicker2.Value.AddYears(1);

                if ("HJKLhjkl".Contains(e.KeyChar.ToString()))
                {
                    RunProjection(false);
                    e.Handled = true;
                }
            }
            else
            {
                if (e.KeyChar == (int) Keys.Escape)
                {
                    tabControlStatistics.SelectedTab = tabPageStatistics;
                    e.Handled = true;
                    return;
                }
            }
        }

        #endregion

    }
}
